package com.java.oplog.config;

import cn.hutool.core.util.StrUtil;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * MongoDB分页插件，需要结合Spring-data使用.
 **/
@Component
public class MongoPageHelper {

    private static final int FIRST_PAGE_NUM = 1;
    private static final String ID = "_id";
    private static MongoTemplate mongoTemplate;

    @Autowired
    public MongoPageHelper(MongoTemplate mongoTemplate) {
        MongoPageHelper.mongoTemplate = mongoTemplate;
    }

//    public MongoPageHelper(MongoTemplate mongoTemplate) {
//        this.mongoTemplate = mongoTemplate;
//    }


    /**
     * 分页查询，直接返回集合类型的结果.
     */
    public static <T> PageResult<T> pageQuery(Query query, Class<T> entityClass, Integer pageSize, Integer pageIndex) {
        return pageQuery(query, entityClass, pageSize, pageIndex, Function.identity(), null);
    }

    /**
     * 分页查询，不考虑条件分页，直接使用skip-limit来分页.
     */
    public static <T, R> PageResult<R> pageQuery(Query query, Class<T> entityClass, Integer pageSize, Integer pageIndex, Function<T, R> mapper) {
        return pageQuery(query, entityClass, pageSize, pageIndex, mapper, null);
    }

    /**
     * 分页查询.
     *
     * @param query       Mongo Query对象，构造你自己的查询条件.
     * @param entityClass Mongo collection定义的entity class，用来确定查询哪个集合.
     * @param mapper      映射器，你从db查出来的list的元素类型是entityClass, 如果你想要转换成另一个对象，比如去掉敏感字段等，可以使用mapper来决定如何转换.
     * @param pageSize    分页的大小.
     * @param pageIndex   当前页.
     * @param lastId      条件分页参数, 区别于skip-limit，采用find(_id>lastId).limit分页.
     *                    如果不跳页，像朋友圈，微博这样下拉刷新的分页需求，需要传递上一页的最后一条记录的ObjectId。 如果是null，则返回pageIndex那一页.
     * @param <T>         collection定义的class类型.
     * @param <R>         最终返回时，展现给页面时的一条记录的类型。
     * @return PageResult，一个封装page信息的对象.
     */
    public static <T, R> PageResult<R> pageQuery(Query query, Class<T> entityClass, Integer pageSize, Integer pageIndex, Function<T, R> mapper, String lastId) {
        //分页逻辑
        Long total = mongoTemplate.count(query, entityClass);
        final Integer pages = (int) Math.ceil(total / (double) pageSize);
        if (pageIndex <= 0 || pageIndex > pages) {
            pageIndex = FIRST_PAGE_NUM;
        }
        final Criteria criteria = new Criteria();
        if (StrUtil.isNotBlank(lastId)) {
            if (pageIndex != FIRST_PAGE_NUM) {
                criteria.and(ID).gt(new ObjectId(lastId));
            }
            query.limit(pageSize);
        } else {
            int skip = pageSize * (pageIndex - 1);
            query.skip(skip).limit(pageSize);
        }

        final List<T> entityList = mongoTemplate.find(query.addCriteria(criteria).with(Sort.by(Direction.ASC, ID)), entityClass);

        final PageResult<R> pageResult = new PageResult<>();
        pageResult.setTotalRows(Integer.valueOf(String.valueOf(total)));
        pageResult.setTotalPage(pages);
        pageResult.setPageSize(pageSize);
        pageResult.setPageIndex(pageIndex);
        pageResult.setRows(entityList.stream().map(mapper).collect(Collectors.toList()));
        return pageResult;
    }

}